#include "push_curl.h"

map<long, map<string, scaler *>> msc;

void init_scaler_map(long addr)
{
    if (msc.count(addr) <= 0)
    {
        scaler *total = new scaler;
        scaler *second = new scaler;
        msc[addr]["total"] = total;
        msc[addr]["second"] = second;
    }
}

void push_curl_init_handler(zend_execute_data *execute_data, long execute_micro_time, string class_name, string function_name)
{
    // orig_curl_init(INTERNAL_FUNCTION_PARAM_PASSTHRU);
}

void push_curl_setopt_handler(zend_execute_data *execute_data, long execute_micro_time, string class_name, string function_name)
{
    zval *ch, *opt, *value;
    string method = "GET";
    string item_name = ITEM_HTTP_METHOD;

    ch = func_get_arg(execute_data, 1);
    opt = func_get_arg(execute_data, 2);
    value = func_get_arg(execute_data, 3);

    long opt_value = opt == nullptr ? 0 : Z_LVAL_P(opt);
    if (PUSH_CURLOPT_POST == opt_value)
    {
        switch (Z_TYPE_P(value))
        {
        case IS_BOOL:
            method = Z_BVAL_P(value) ? VALUE_HTTP_METHOD_POST : VALUE_HTTP_METHOD_GET;
            break;
        case IS_STRING:
            method = Z_LVAL_P(value);
        case IS_LONG:
            method = Z_LVAL_P(value) ? VALUE_HTTP_METHOD_POST : VALUE_HTTP_METHOD_GET;
            break;
        default:
            method = VALUE_HTTP_METHOD_GET;
            break;
        }
    }

    if (ch != nullptr)
    {
        long addr = reinterpret_cast<long>(ch);

        init_scaler_map(addr);

        msc[addr]["total"]->addItem(item_name, method);
        msc[addr]["second"]->addItem(item_name, method);
    }
}

void push_curl_setopt_array_handler(zend_execute_data *execute_data, long execute_micro_time, string class_name, string function_name)
{
    zval *ch, *arr;
    string method = "GET";
    string item_name = ITEM_HTTP_METHOD;

    ch = func_get_arg(execute_data, 1);
    arr = func_get_arg(execute_data, 2);

    if (arr != nullptr)
    {
        zval **value;
        if (SUCCESS == zend_hash_index_find(HASH_OF(arr), PUSH_CURLOPT_POST, (void **)&value))
        {
            switch (Z_TYPE_PP(value))
            {
            case IS_BOOL:
                method = Z_BVAL_PP(value) ? VALUE_HTTP_METHOD_POST : VALUE_HTTP_METHOD_GET;
                break;
            case IS_STRING:
                method = Z_LVAL_PP(value);
            case IS_LONG:
                method = Z_LVAL_PP(value) ? VALUE_HTTP_METHOD_POST : VALUE_HTTP_METHOD_GET;
                break;
            default:
                method = VALUE_HTTP_METHOD_GET;
                break;
            }
        }

        if (ch != nullptr)
        {
            long addr = reinterpret_cast<long>(ch);

            init_scaler_map(addr);

            msc[addr]["total"]->addItem(item_name, method);
            msc[addr]["second"]->addItem(item_name, method);
        }
    }
}

void push_curl_exec_handler(zend_execute_data *execute_data, long execute_micro_time, string class_name, string function_name)
{
    zval *ch = func_get_arg(execute_data, 1);
    if (ch == nullptr)
    {
        return;
    }

    // 执行系统 curl_getinfo 函数
    zval func;
    zval *args[1];
    zval curl_info;
    args[0] = ch;
    ZVAL_STRING(&func, "curl_getinfo", 0);
    call_user_function(EG(function_table), NULL, &func, &curl_info, 1, args TSRMLS_CC);

    zval **z_url, **z_http_code, **z_total_time, **z_primary_ip, **z_primary_port;
    string url = "unknown", http_code = "unknown", total_time = "unknown", primary_ip = "unknown", primary_port = "unknown";

    if (SUCCESS == zend_hash_find(HASH_OF(&curl_info), "url", sizeof("url"), (void **)&z_url))
    {
        url = Z_STRVAL_PP(z_url);
    }
    if (SUCCESS == zend_hash_find(HASH_OF(&curl_info), "http_code", sizeof("http_code"), (void **)&z_http_code))
    {
        http_code = to_string(Z_LVAL_PP(z_http_code));
    }
    if (SUCCESS == zend_hash_find(HASH_OF(&curl_info), "total_time", sizeof("total_time"), (void **)&z_total_time))
    {
        total_time = doubleToString(Z_DVAL_PP(z_total_time), PUSH_HISTOGRAM_DECIAML);
    }
    if (SUCCESS == zend_hash_find(HASH_OF(&curl_info), "primary_ip", sizeof("primary_ip"), (void **)&z_primary_ip))
    {
        primary_ip = Z_STRVAL_PP(z_primary_ip);
    }
    if (SUCCESS == zend_hash_find(HASH_OF(&curl_info), "primary_port", sizeof("primary_port"), (void **)&z_primary_port))
    {
        primary_port = to_string(Z_LVAL_PP(z_primary_port));
    }

    zval url_func;
    zval url_info;
    zval *url_args[1];
    url_args[0] = *z_url;
    ZVAL_STRING(&url_func, "parse_url", 0);
    call_user_function(EG(function_table), NULL, &url_func, &url_info, 1, url_args TSRMLS_CC);

    string url_scheme = "", url_host = "", url_port = "", url_path = "", url_query = "", url_full_path = "";
    zval **z_url_scheme, **z_url_host, **z_url_port, **z_url_path, **z_url_query;
    if (SUCCESS == zend_hash_find(HASH_OF(&url_info), "scheme", sizeof("scheme"), (void **)&z_url_scheme))
    {
        url_scheme = Z_STRVAL_PP(z_url_scheme);
    }
    if (SUCCESS == zend_hash_find(HASH_OF(&url_info), "host", sizeof("host"), (void **)&z_url_host))
    {
        url_host = Z_STRVAL_PP(z_url_host);
    }
    if (SUCCESS == zend_hash_find(HASH_OF(&url_info), "port", sizeof("port"), (void **)&z_url_port))
    {
        url_port = to_string(Z_LVAL_PP(z_url_port));
    }
    if (SUCCESS == zend_hash_find(HASH_OF(&url_info), "path", sizeof("path"), (void **)&z_url_path))
    {
        url_path = Z_STRVAL_PP(z_url_path);
    }
    if (SUCCESS == zend_hash_find(HASH_OF(&url_info), "query", sizeof("query"), (void **)&z_url_query))
    {
        url_query = Z_STRVAL_PP(z_url_query);
    }
    url_full_path = url_query == "" ? url_path : url_path + "?" + url_query;

    if (url_port == "")
    {
        if (url_scheme == "https")
        {
            url_port = "443";
        }
        else if (url_scheme == "http")
        {
            url_port = "80";
        }
    }

    long addr = reinterpret_cast<long>(ch);

    init_scaler_map(addr);

    msc[addr]["second"]->addItem(ITEM_HTTP_ADDRESS, primary_ip + ":" + primary_port);
    msc[addr]["second"]->addItem(ITEM_HTTP_FULL_PATH, url_full_path);
    msc[addr]["second"]->addItem(ITEM_HTTP_PATH, url_path);
    msc[addr]["second"]->addItem(ITEM_HTTP_PEER, url_host + ":" + url_port);
    msc[addr]["second"]->setIndex(INDEX_HTTP_CLIENT_HANDLING_SECCONDS);

    msc[addr]["total"]->addItem(ITEM_HTTP_CODE, http_code);
    msc[addr]["total"]->addItem(ITEM_HTTP_ADDRESS, primary_ip + ":" + primary_port);
    msc[addr]["total"]->addItem(ITEM_HTTP_FULL_PATH, url_full_path);
    msc[addr]["total"]->addItem(ITEM_HTTP_PATH, url_path);
    msc[addr]["total"]->addItem(ITEM_HTTP_PEER, url_host + ":" + url_port);
    msc[addr]["total"]->setIndex(INDEX_HTTP_CLIENT_HANDLED_TOTAL);

    client *pushgateway_client = client::newInstance();
    string bucket = PUSHGATEWAY_G(bucket);
    pushgateway_client->setBucket(msc[addr]["second"], Z_DVAL_PP(z_total_time), bucket);
    pushgateway_client->setCounter(msc[addr]["total"]);
}

void push_curl_close_handler(zend_execute_data *execute_data, long execute_micro_time, string class_name, string function_name)
{
    // 获取函数传参
    zval *ch = func_get_arg(execute_data, 1);
    if (ch != nullptr)
    {
        long addr = reinterpret_cast<long>(ch);
        msc.erase(addr);
    }
}